<?php
// +----------------------------------------------------------------------+
// | Author:  Evgeny Leontev <eleontev@gmail.com>                         |
// | Copyright (c) 2005 Evgeny Leontev                                    |
// +----------------------------------------------------------------------+
// | This source file is free software; you can redistribute it and/or    |
// | modify it under the terms of the GNU Lesser General Public           |
// | License as published by the Free Software Foundation; either         |
// | version 2.1 of the License, or (at your option) any later version.   |
// |                                                                      |
// | This source file is distributed in the hope that it will be useful,  |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
// | Lesser General Public License for more details.                      |
// +----------------------------------------------------------------------+

class AuthPriv extends AppModel
{
	var $sql;
	
	var $as_name = 'member_';          //session prefix
	var $auth;
	var $thua;
	var $bad_attempt = 3;
	
	var $ps_name = 'priv_';
	var $priv_area;						// news, user, ...
	var $user_id;						
	
	var $own_sql = false;				// to make check for owm records
	var $own_use = false;				// to make check for owm records
	var $own_field;						// author field in table
	var $own_param = '1=1';				// select all records
	
	var $priv_actions = array('select','insert','update','status','delete'); 
	var $custom_actions = array();
	
	var $tables = array('user', 'priv', 'priv_rule', 'priv_name', 'priv_module');
	var $tbl;
	var $cookie_expire = '3 month';

	var $skip_auth_expired;
	var $ip = '';
	
	
	function AuthPriv() {
		parent::AppModel();
		$this->setCustomAction('detail', 'select');
	}

	
	// return true or false
//	function doAuth($username, $password, $remember = false) {
//
//		$auth = false;
//
//		if (!empty($username) && !empty($password)) {
//
//			$sql = "
//			SELECT
//				u.id AS user_id,
//				u.username,
//				n.name AS priv_name,
//				n.id AS priv_id
//			FROM
//				{$this->tbl->user} u,
//				{$this->tbl->priv_name} n,
//				{$this->tbl->priv} p
//			WHERE u.username = '%s'
//			AND u.password = '%s'
//			AND u.id = p.user_id
//			AND n.id = p.priv_name_id
//			AND n.active = 1
//			AND u.active = 1";
//
//			$sql = sprintf($sql, mysql_escape_string($username), MD5($password));
//			$result =& $this->db->Execute($sql) or die(db_error($sql));
//			$row =& $result->FetchRow();
//
//			//$result = mysql_query($sql) or die (db_error($sql));
//			//$row = mysql_fetch_assoc($result);
//
//			//echo "<pre>"; print_r($sql); echo "</pre>";
//			//echo "<pre>"; print_r($row); echo "</pre>";
//			//echo "<pre>"; print_r($result); echo "</pre>";
//			//exit;
//
//			if ($result->RecordCount() == 1) {
//				$auth = true;
//				$this->authToSession($row['user_id'], $row['username'], $row['priv_name'], $row['priv_id']);
//				$this->privToSession($row['user_id']);
//
//				if($remember) {
//					$this->authToCookie($this->cookie_expire, $row['user_id']);
//				}
//			}
//		}
//
//		return $auth;
//	}

	function doAuth($username, $password, $remember = false) {

		$auth = false;

		if (!empty($username) && !empty($password)) {

			$sql = "
			SELECT
				u.id AS user_id,
				u.username,
				u.role_id,
				n.name AS priv_name,
				n.id AS priv_id
			FROM
				{$this->tbl->user} u,
				{$this->tbl->priv_name} n,
				{$this->tbl->priv} p
			WHERE u.username = '%s'
			AND u.password = '%s'
			AND u.id = p.user_id
			AND n.id = p.priv_name_id
			AND n.active = 1
			AND u.active = 1";

			$sql = sprintf($sql, mysql_escape_string($username), MD5($password));
			$sql = ($this->sql) ? $this->sql : $sql;
			$result =& $this->db->Execute($sql) or die(db_error($sql));
			$row =& $result->FetchRow();

			if ($result->RecordCount() == 1) {
				$auth = true;
				$this->authToSession($row['user_id'], $row['username'], $row['role_id'], $row['priv_name'], $row['priv_id']);
				$this->privToSession($row['user_id']);

				if($remember) {
					$this->authToCookie($this->cookie_expire, $row['user_id']);
				}
			}

		}

		return $auth;
	}
	
	function getUserByValue($field, $value, $use_md5 = true) {
		
		if($use_md5) { $sql = "md5(%s)='%s'"; }
		else         { $sql = "%s='%s'"; }
		$where_sql = sprintf($sql, $field, mysql_escape_string($value));
		
		$sql = "SELECT * FROM {$this->tbl->user} WHERE {$where_sql}";
		$result =& $this->db->Execute($sql) or die(db_error($sql));		
		return $result->FetchRow();
	}
	
	
	function doAuthByValue($field, $value, $use_md5 = true, $remember = false) {
		$user = $this->getUserByValue($field, $value, $use_md5);		
		return $this->_doAuthByUserId($user['id'], $remember);
	}
	
	
//	function _doAuthByUserId($user_id, $remember = false) {
//
//		$auth = false;
//
//		if (!empty($user_id)) {
//
//			$sql = "
//			SELECT
//				u.id AS user_id,
//				u.username,
//				n.name AS priv_name,
//				n.id AS priv_id
//			FROM
//				{$this->tbl->user} u,
//				{$this->tbl->priv_name} n,
//				{$this->tbl->priv} p
//			WHERE u.id = '%d'
//			AND u.id = p.user_id
//			AND n.id = p.priv_name_id
//			AND n.active = 1
//			AND u.active = 1";
//
//			$sql = sprintf($sql, $user_id);
//			$result =& $this->db->Execute($sql) or die(db_error($sql));
//			$row =& $result->FetchRow();
//
//			if ($result->RecordCount() == 1) {
//				$auth = true;
//				$this->authToSession($row['user_id'], $row['username'], $row['priv_name'], $row['priv_id']);
//				$this->privToSession($row['user_id']);
//
//				if($remember) {
//					$this->authToCookie($this->cookie_expire, $row['user_id']);
//				}
//			}
//		}
//
//		return $auth;
//	}

	function _doAuthByUserId($user_id, $remember = false) {
		$auth = false;

		if (!empty($user_id)) {
			$sql = "
			SELECT
				u.id AS user_id,
				u.username,
                u.role_id,
				n.name AS priv_name,
				n.id AS priv_id
			FROM
				{$this->tbl->user} u,
				{$this->tbl->priv_name} n,
				{$this->tbl->priv} p
			WHERE u.id = '%d'
			AND u.id = p.user_id
			AND n.id = p.priv_name_id
			AND n.active = 1
			AND u.active = 1";

			$sql = sprintf($sql, $user_id);
			$result =& $this->db->Execute($sql) or die(db_error($sql));
			$row =& $result->FetchRow();

			if ($result->RecordCount() == 1) {
				$auth = true;
				$this->authToSession($row['user_id'], $row['username'], $row['role_id'], $row['priv_name'], $row['priv_id']);

				$this->privToSession($row['user_id']);

				if($remember) {
					$this->authToCookie($this->cookie_expire, $row['user_id']);
				}
			}
		}

		return $auth;
	}
	
	function doAuthByCookie() {
		
		$auth = $this->_doAuthByUserId($this->getCookie());
		
		if($auth == false) {
			$this->removeCookie();
		}
		
		return $auth;
	}

	
	function setPrivArea($priv_area) {
		$this->priv_area = $priv_area;
		$this->user_id = $_SESSION[$this->as_name]['user_id'];
		$this->own_use = false;		
	}
	
	
	// set sql for select own records
	function setOwnParam($own_param) {
		$this->own_param = $own_param;
		$this->own_use = true;
	}	
	
	
	// set sql for unable to edit not own records 
	function setOwnSql($sql) {
		$this->own_sql = $sql;
	}
	
	// get own param, should be used in selecting records
	function getOwnParam($priv_area = false) {
		$param = ($this->isSelfPriv('select', $priv_area)) ? $this->own_param : '1=1';
		return $param;
	}
	
	
	// user id for certain record
	function _selectUserId($sql = false) {
		$result =& $this->db->Execute($sql) or die(db_error($sql));
		$priv = $result->RecordCount();	
		return $priv;
	}
	
	
	// if we need to change some actions to common actions
	function setCustomAction($custom_action, $priv_action) {
		$this->custom_actions[$custom_action] = $priv_action;
	}
	
	
	// $author_field - field in table where user_id stored
	// in news_record the field is 'author_id'
	function check($priv_action = 'select', $priv_area = false) {
	
		$priv = false;
		$priv_area = ($priv_area) ? $priv_area : $this->priv_area;
		
		// it is not priv area we need not to check
		//if($this->isNotPrivArea($this->priv_area)) { return; }
		
		// change to commom action if needed
		$priv_action = (array_key_exists($priv_action, $this->custom_actions)) ? $this->custom_actions[$priv_action] : $priv_action;
		
		// if not acction assigned change to select
		if(empty($priv_action)) { $priv_action = 'select'; }
		

		// check for wrong param to functions 
		if(!in_array($priv_action, $this->priv_actions)) {
			exit("Wrong param fo function CheckPriv::check($priv_action). Correct it!");
		}
		
		// if we have check_priv = 0 in db (no priv area)
		if($priv_area === false) {
			return true;
		}
		
		// just if isset this chapters to enter
		if(isset($_SESSION[$this->ps_name][$priv_area]) || isset($_SESSION[$this->ps_name]['all'])) { 
			
			if($this->privInArray($priv_action, $priv_area)) {
				$priv = true; 
		
			// check if only his record can modify
			} elseif($this->privInArray('self_' . $priv_action, $priv_area)) { 
				
				// for delete, update, change_status only own records
				if($priv_action == 'update' || $priv_action == 'delete' || $priv_action == 'status') {
					$priv = ($this->own_sql) ? $this->_selectUserId($this->own_sql) : true;
					//echo 2;
								
				} elseif($priv_action == 'select') {
					$priv = true;
				}
			}
		}
		
		
		//echo "<pre>"; print_r($this); echo "</pre>";
		if(!$priv) { 
			echo $this->errorMsg();
			exit();
		}
	}
	
	
	// 
	function CheckSimpleStatus() {
		$priv = $this->privInArray('status');
		return $priv;  	
	}

	
	// return true if user has self - privileges
	function isSelfPriv($priv_action, $priv_area = false) {
		return $this->privInArray('self_' . $priv_action, $priv_area);
	}
	
	
	// check for certain priv action 
	function checkPrivAction($priv_action, $priv_area = false) {
		
		$priv = false;
		if($this->privInArray($priv_action, $priv_area)) {
			$priv = true; 
	
		// check if only his record can modify
		} elseif($this->privInArray('self_' . $priv_action, $priv_area)) { 
			
			// for delete, update, change_status only own records
			if($priv_action == 'update' || $priv_action == 'delete' || $priv_action == 'status') {
				$priv = ($this->own_sql) ? $this->_selectUserId($this->own_sql) : true;
							
			} elseif($priv_action == 'select') {
				$priv = true;
			}
		}
		
		return $priv;
	}
	
	
	// we use it AppView 
	// here we do not check for real self priv
	function isPriv($priv_action, $priv_area = false) {
		
		$priv = false;
		if($this->privInArray($priv_action, $priv_area)) {
			$priv = true;
		} elseif($this->privInArray('self_' . $priv_action, $priv_area)) {
			$priv = true;
		}
		
		return $priv;
	}
	
	
	// only specified priv
	function isPrivConcrete($priv_action, $priv_area = false) {
		
		$priv = false;
		if($this->privInArray($priv_action, $priv_area)) {
			$priv = true;
		}
		
		return $priv;
	}	
	
	// Returns TRUE if user has priv($priv_action), FALSE otherwise
	function privInArray($priv_action, $priv_area = false) {
		$priv = false;
		$priv_area = ($priv_area) ? $priv_area : $this->priv_area;
		
		if(isset($_SESSION[$this->ps_name]['all'])) {
			if(in_array($priv_action, $_SESSION[$this->ps_name]['all'])) {
				$priv = true;
			}
			
		} elseif(!isset($_SESSION[$this->ps_name][$priv_area])) {
			$priv = false;
		
		} elseif (in_array($priv_action, $_SESSION[$this->ps_name][$priv_area])) {
			$priv = true;
		}
		return $priv;
	}
	
	
	// select for priv  # creating array with priv for current user
	function privToSession($user_id){
		
		$sql = "SELECT m.module_name, r.what_priv, r.apply_to_child, m.id
		FROM {$this->tbl->priv} p, 
			 {$this->tbl->priv_rule} r, 
			 {$this->tbl->priv_name} n,
			 {$this->tbl->priv_module} m
		WHERE n.active = 1
		AND r.active = 1
		AND p.user_id = %d
		AND m.id = r.priv_module_id
		AND n.id = p.priv_name_id
		AND n.id = r.priv_name_id";
		
		$sql = sprintf($sql, $user_id);
		
		$this->db->SetFetchMode(ADODB_FETCH_NUM);
		$result = &$this->db->Execute($sql) or die(db_error($sql));
		$this->db->SetFetchMode(ADODB_FETCH_ASSOC);
		
		while($row = $result->FetchRow()){
			
			// creating session $_SESSION['module'] => array(what_priv)
			$_SESSION[$this->ps_name][$row[0]] = explode(',',$row[1]);
			
			if($row['0'] == 'all' || $row[2] == 0) { continue; }
			$to_childs_ar[] = array('id'=>$row[3], 'name'=>$row[0]);
		}
		
		if(isset($to_childs_ar)) {
			$tree_ar =& $this->getCheckPrivTreeArray();
			foreach($to_childs_ar as $k => $v) {
				$this->childPrivToSession($tree_ar, $v['name'], $v['id']);
			}
		}
		
		if(!isset($_SESSION[$this->ps_name]['all'])) {
			$this->parentPrivToSession();
		}
	}

	
	// place all parent modules in session
	// to be able to show menu, sub menu etc.
	function parentPrivToSession() {
				
		// another variant no matter how tree deep
		// not sure what is better, looks much beautiful :)
		$sql = "SELECT 
		p1.module_name AS parent,
		p2.module_name AS child 
		FROM {$this->tbl->priv_module} p1
		LEFT JOIN {$this->tbl->priv_module} p2 ON p2.parent_id = p1.id
		WHERE p2.check_priv = 1";
		$result = &$this->db->Execute($sql) or die(db_error($sql));
		while($row = $result->FetchRow()){
			
			if(!$row['child']) { continue; }
				
			if(isset($_SESSION[$this->ps_name][$row['child']]) && !isset($_SESSION[$this->ps_name][$row['parent']]) && $row['parent'] != 'all') {
				$_SESSION[$this->ps_name][$row['parent']][] = 'select';
				mysql_data_seek ($result, 0);
			}
		}	
	}
	
	
	// return priv area starting from last parameter
	// as parameters get possible values getPrivArea($val,$val2);
	function getPrivArea($val) {
		
		$args = func_get_args();
		$numargs = func_num_args();
		
		// in some places we have the same values for $val1, $val2
		// to avoid it and generate apropriate sql
		for($i=0;$i<$numargs;$i++){
			if(isset($args[$i+1])) {
				if($args[$i] == $args[$i+1]) {
					unset($args[$i+1]);
				}
			}
		}
		
		for($i=0;$i<$numargs;$i++){
			if(empty($args[$i])) { continue; }
			
			$as_sql[]  = "p{$i}.module_name AS module_{$i}, p{$i}.check_priv AS check_{$i}";
			$where_sql[] = "AND p{$i}.module_name = '{$args[$i]}'";
			if($i!=0) {
				$a = $i-1;
				$join_sql[] = "LEFT JOIN {$this->tbl->priv_module} p{$i} ON p{$i}.parent_id = p{$a}.id";
			}
		}
		
		$as_sql = (isset($as_sql)) ? implode(',', $as_sql) : 'p0.module_name AS module_0, p0.check_priv AS check_0';
		$join_sql = (isset($join_sql)) ? implode(' ', $join_sql) : '';
		$where_sql = (isset($where_sql)) ? implode(' ', $where_sql) : "AND p0.module_name = 'knowlegebase'";
		
		$sql = "SELECT {$as_sql} FROM {$this->tbl->priv_module} p0 {$join_sql} 
		WHERE p0.parent_id = '0' {$where_sql}";
		//echo "<pre>"; print_r($sql); echo "</pre>";
		
		$result = &$this->db->Execute($sql) or die(db_error($sql));
		$row = $result->FetchRow();
		
		$priv_area = false;
		$num = (count($row)/2)-1;
		for($i=$num;$i>=0;$i--){
			if($row['check_' . $i] == 1) {
				$priv_area = $row['module_' . $i];
				break;
			}
		}
		
		return $priv_area;
	}
	
	
	// return array with all modules that need to be check fro priv
	function & getCheckPrivTreeArray() {
		
		$sql = "SELECT * FROM {$this->tbl->priv_module} WHERE check_priv = 1 ORDER BY sort_order";
		$result = &$this->db->Execute($sql) or die(db_error($sql));
		while($row = $result->FetchRow()){
			$array[$row['parent_id']][$row['id']] = $row['module_name'];
		}
		
		return $array;
	}
	
	
	// set priv session to all child modules from 
	function childPrivToSession($array, $module_name, $parent_id = 0) {
		foreach($array[$parent_id] as $id => $v) { 
			$_SESSION[$this->ps_name][$v] = $_SESSION[$this->ps_name][$module_name];
			
			if(isset($array[$id])){
				$this->childPrivToSession($array, $v, $id);
			}
		}
	}
	
	
	function authToSession($user_id, $username, $role_id, $priv_name, $priv_id = false) {
		$_SESSION[$this->as_name]['auth'] = md5(session_id() . $username . $this->ip);
		$_SESSION[$this->as_name]['thua'] = session_id();
		$_SESSION[$this->as_name]['user_id'] = $user_id; 	// for chekPriv();
		$_SESSION[$this->as_name]['username'] = $username; 
		$_SESSION[$this->as_name]['role_id'] = $role_id;
		$_SESSION[$this->as_name]['priv_name'] = $priv_name;
		$_SESSION[$this->as_name]['priv_id'] = $priv_id;
		$_SESSION[$this->as_name]['time_flag'] = time(); 		// time when entering admin area using in spentTime();
	}
	
	
	function setCookieExpire($period = false) {
		$this->cookie_expire = $this->getCookieTime($period);
	}
	
	
	// period - 1 hour, 5 days, 8 months, 2 years ...
	function getCookieTime($period = false, $sign = '+') {
		return ($period) ? strtotime($sign . $period) : time();
	}
	
	
	function removeCookie() {
		$this->authToCookie();
	}
	
	
	function authToCookie($period = false, $user_id = false, $path = '/') {
		//setcookie ( string name [, string value [, int expire [, string path [, string domain [, int secure]]]]] )
		setcookie('auth_c_[user_id]', $user_id, $this->getCookieTime($period), $path);
	}
	
	
	function getCookie() {
		return @$_COOKIE['auth_c_']['user_id'];
	}
	
	function getRoleId() {
		return @$_SESSION[$this->as_name]['role_id'];
	}
	
	function _isAuth() {
		$ret = true;
		if(@$_SESSION[$this->as_name]['auth'] != md5(@$_SESSION[$this->as_name]['thua'] . @$_SESSION[$this->as_name]['username'] . $this->ip) 
		   || @!$_SESSION[$this->as_name]['auth'] || @!$_SESSION[$this->as_name]['thua']){
		
			$ret = false;
		}
		return $ret;		
	}
	

	function isAuth(){
		
/*
		$ret = false;
		
		// we have cookie and no auth
		if(!$this->_isAuth() && $this->_isCookie()) {
			
		
		} elseif ($this->_isAuth()) {
			$ret = true;
		}
*/
		$ret = true;
		if(@$_SESSION[$this->as_name]['auth'] != md5(@$_SESSION[$this->as_name]['thua'] . @$_SESSION[$this->as_name]['username'] . $this->ip)
		   || @!$_SESSION[$this->as_name]['auth'] || @!$_SESSION[$this->as_name]['thua']){

			$ret = false;
		}
		return $ret;
		
//		return $this->_isAuth();
	}
	
	
	function getUsername() {
		return $_SESSION[$this->as_name]['username'];
	}
	
	
	function getUserId() {
		return $_SESSION[$this->as_name]['user_id'];
	}
	
	
	function getPrivId() {
		return $_SESSION[$this->as_name]['priv_id'];
	}
	
/*
	function badAtempt($num = false) {
	
		$bad_num = false;
		if($num) { $this->bad_attempt = $num; }
		$a = @$_SESSION[$this->as_name]['bad_auth']++;
		if($a >= $this->bad_attempt) {
			$bad_num true;
		}
		return $bad_num;
	}
*/
	
	
	function setSkipAuthExpired($val) {
		$this->skip_auth_expired = ($this->skip_auth_expired || $val);
	}
	
	
	// spentTime(session var[,time in minute]){
	// Unset all sessions if curent time > "time in minute"
	// if $how_long = false we not use it
	function setAuthExpired($how_long){
		if($how_long && !$this->skip_auth_expired) {
			$exp_time = @$_SESSION[$this->as_name]['time_flag']+$how_long*60;
			$cur_time = time();
			
			if($cur_time < $exp_time){
				$_SESSION[$this->as_name]['time_flag'] = time();
			} else {
				$_SESSION[$this->as_name] = array();
			}
		}
	}
		
	
	function errorMsg() {
		
		//if(!file_exists($file)) { return; }
		
		require_once 'eleontev/HTML/BoxMsg.php';
		
		//$msg =& GetMsgHelper::parseMultiIni($file, $keyword);
		//if($msg) {
			
		$msgs = array('title'=>'ACCESS DENIED!', 
		              'body' =>'You do not have enough permissions for this operation or access this module.<br>
					            <b>&laquo; <a href="javascript:history.back(1)">back</a></b>');
		return ErrorMsg::quickGet($msgs);
	}

	function getIP() {
		//return WebUtil::getIP();
		return '';
	}

	function setSql($sql) {
		$this->sql = $sql;
	}

	function setTable($table) {
		$this->tbl->table = $table;
	}

    function logout() {
		unset($_SESSION[$this->as_name]);
	}

	function spentTime($how_long = 0) {
		if($how_long && isset($_SESSION[$this->as_name]) && !$this->getCookie()) {
			$exp_time = @$_SESSION[$this->as_name]['time_flag']+$how_long*60;
			$cur_time = time();

			if($cur_time < $exp_time){
				$_SESSION[$this->as_name]['time_flag'] = time();
			} else {
				$_SESSION[$this->as_name] = array();
				unset($_SESSION[$this->as_name]);
			}
		}
	}


}
?>